using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Reflection;
using System.Buffers;
using System.Linq;

namespace BNode.Expressions
{
    public class ExprParametersInfo
    {
        /// <summary>
        /// 在封装为Lambda时传入
        /// </summary>
        protected internal readonly List<ParameterExpression> _parameters=new();
        /// <summary>
        /// 在封装为Block时传入
        /// </summary>
        protected internal readonly List<ParameterExpression> _variables=new();
        //引用的参数和变量只用于查找和替换。无需传入
        protected readonly List<ParameterExpression> _ref_parameters;
        protected readonly List<ParameterExpression> _ref_variables;

        public int ParamCount => _parameters.Count;
        public int VarCounter => _variables.Count;

        public IEnumerable<ParameterExpression> Parameters() => _parameters;
        public ParameterExpression Parameters(int index) => _parameters[index];
        public ParameterExpression Parameters(string name) => SelfAndRefParameters.FirstOrDefault(p => p.Name == name);
        public IEnumerable<ParameterExpression> Variables() => _variables;
        public ParameterExpression Variables(int index) => _variables[index];
        public ParameterExpression Variables(string name) => SelfAndRefVariables.FirstOrDefault(v => v.Name == name);
        public IEnumerable<ParameterExpression> SelfAndRefParameters
            => _ref_parameters == null ? _parameters : _parameters.Concat(_ref_parameters);
        public IEnumerable<ParameterExpression> SelfAndRefVariables
            => _ref_variables == null ? _variables : _variables.Concat(_ref_variables);
        public ExprParametersInfo() { }
        public ExprParametersInfo(ExprParametersInfo refBase)
        {
            _ref_parameters = new();
            _ref_variables = new();
            _ref_parameters.AddRange(refBase.SelfAndRefParameters);
            _ref_variables.AddRange(refBase.SelfAndRefVariables);
        }
        public ExprParametersInfo(IEnumerable<ParameterExpression> refParameters, IEnumerable<ParameterExpression> refVariables)
        {
            _ref_parameters = new();
            _ref_variables = new();
            _ref_parameters.AddRange(refParameters);
            _ref_variables.AddRange(refVariables);
        }
        public void AddParameter(Type type, string name = null)
        {
            name ??= $"p{ParamCount + 1}";
            var param = Expression.Parameter(type, name);
            _parameters.Add(param);
        }
        public void AddParameter(Type type, out ParameterExpression parameter, string name)
        {
            parameter = Expression.Parameter(type, name);
            _parameters.Add(parameter);
        }
        public void AddParameter(ParameterExpression parameter)
        {
            _parameters.Add(parameter);
        }
        public void DeclareVariable(Type type, string name = null)
        {
            name ??= $"var{VarCounter + 1}";
            var variable = Expression.Variable(type, name);
            _variables.Add(variable);
        }
        public void DeclareVariable(Type type, out ParameterExpression variable, string name)
        {
            variable = Expression.Variable(type, name);
            _variables.Add(variable);
        }
        public void DeclareVariable(ParameterExpression variable)
        {
            _variables.Add(variable);
        }
        public void GetOrAddVariable(Type type, out ParameterExpression parameter, string name)
        {
            parameter = _variables.GetOrAdd(t => t.Name == name, () => Expression.Variable(type, name));
        }
        public void Clear()
        {
            _parameters.Clear();
            _ref_parameters?.Clear();
            _variables.Clear();
            _ref_variables?.Clear();
        }
    }

    public class ExpressionsInfo
    {
        public readonly List<Expression> _expressions = new();
        /// <summary>
        /// 最后一条有返回值的表达式
        /// </summary>
        public Expression _lastValueExpression;
        /// <summary>
        /// 最后一条表达式
        /// </summary>
        public Expression LastExpression => _expressions.LastOrDefault();
        /// <summary>
        /// 返回值，如果存在则其一定是最后一条表达式
        /// </summary>
        public Expression _returnValue;

        //如果此表达式树是子表达式树，才可能有标签
        public LabelTarget _breakLabel;
        public LabelTarget _continueLabel;
        public ExpressionsInfo() { }
        public ExpressionsInfo(ExpressionsInfo refBase)
        {
            _breakLabel = refBase._breakLabel;
            _continueLabel = refBase._continueLabel;
        }
        /// <summary>
        /// 添加一条表达式
        /// </summary>
        public void AddExpr(Expression expression)
        {
            _expressions.Add(expression);
        }
        /// <summary>
        /// 添加一条有返回值的表达式
        /// </summary>
        public void AddExprForLast(Expression expression)
        {
            _lastValueExpression = expression;
            _expressions.Add(_lastValueExpression);
        }
        public void Return(Expression expression)
        {
            if (_returnValue != null) new InvalidOperationException("一个表达式树只允许Return一次").Throw();
            if (expression == null) new InvalidOperationException("参数不能为空").Throw();
            _returnValue = expression;
            AddExprForLast(_returnValue);
        }
        public void ReturnLast()
        {
            if (_returnValue != null) new InvalidOperationException("一个表达式树只允许Return一次").Throw();
            if (_lastValueExpression == null) new InvalidOperationException("没有有效表达式作为返回值").Throw();
            if (LastExpression != _lastValueExpression) new InvalidOperationException("最后一条表达式无返回值").Throw();
            _returnValue = _lastValueExpression;
        }
        public void Assign(Expression target, Expression value = null)
        {
            value ??= _lastValueExpression;
            if (value == null) new InvalidOperationException("没有有效表达式为目标赋值").Throw();
            var assign = Expression.Assign(target, value);
            _expressions.Add(assign);
            _lastValueExpression = target;
        }
        public void AddAssign(Expression target, Expression value = null)
        {
            value ??= _lastValueExpression;
            if (value == null) new InvalidOperationException("没有有效表达式为目标赋值").Throw();
            var assign = Expression.AddAssign(target, value);
            _expressions.Add(assign);
            _lastValueExpression = target;
        }
        protected internal Expression GetOrDefaultReturnValue(Type defaultReturnType)
        {
            if (_returnValue != null) return _returnValue;

            //没有手动Return，则视为ReturnLast
            if (_lastValueExpression != null && LastExpression == _lastValueExpression)
            {
                return _lastValueExpression;
            }
            //失败则视为ReturnNull
            return defaultReturnType == typeof(void) ? Expression.Empty() : Expression.Default(defaultReturnType);
        }
        public void Rewrite(Func<Expression, Expression> rewriteRule, out List<Expression> rewritten)
        {
            rewritten = _expressions.Select(rewriteRule).ToList();
            _expressions.Clear();
            _expressions.AddRange(rewritten);
        }
        public void Break()
        {
            if (_breakLabel == null) new NullReferenceException("本块没有跳出标签").Throw();
            AddExpr(Expression.Break(_breakLabel));
        }
        public void Continue()
        {
            if (_continueLabel == null) new NullReferenceException("本块没有跳出标签").Throw();
            AddExpr(Expression.Continue(_continueLabel));
        }
        public void Clear()
        {
            _expressions.Clear();
            _lastValueExpression = null;
            _returnValue = null;
            _breakLabel = null;
            _continueLabel = null;
        }
    }

    //By UNeko
    /// <summary>
    /// 表达式树构建工具。提供更方便的参数变量和函数调用等方法。
    /// </summary>
    public partial class ExprBuilder
    {
        protected readonly ExprParametersInfo _parameterInfo;
        public ExprParametersInfo PInfo => _parameterInfo;
        protected readonly ExpressionsInfo _expressionInfo;
        public ExpressionsInfo EInfo => _expressionInfo;

        protected internal bool _debugMode;

        public ExprBuilder() 
        {
            _parameterInfo = new ExprParametersInfo();
            _expressionInfo = new ExpressionsInfo();
        }
        public ExprBuilder(ExprBuilder refBase)
        {
            if (refBase == null) return;
            //子树继承标签。当为循环子树时会重赋值为新的独立标签
            _expressionInfo = new ExpressionsInfo(refBase._expressionInfo);
            _parameterInfo = new ExprParametersInfo(refBase._parameterInfo);
            _debugMode = refBase._debugMode;
        }

        /// <summary>
        /// 结束构建，返回一条表达式（必须有返回值）
        /// <para>表达式树会自动在末尾进行返回，所以请仅在明确希望表达式树不再受到任何添加时执行Return</para>
        /// </summary>
        public ExprBuilder Return(Expression expression)
        {
            EInfo.Return(expression);
            return this;
        }
        /// <summary>
        /// 结束构建，返回最后一条表达式（必须有返回值）
        ///  <para>表达式树会自动在末尾进行返回，所以请仅在明确希望表达式树不再受到任何添加时执行Return</para>
        /// </summary>
        public ExprBuilder ReturnLast()
        {
            EInfo.ReturnLast();
            return this;
        }
        /// <summary>
        /// 结束构建，返回空值
        ///  <para>表达式树会自动在末尾进行返回，所以请仅在明确希望表达式树不再受到任何添加时执行Return</para>
        /// </summary>
        public ExprBuilder ReturnNull()
        {
            EInfo.Return(Expression.Empty());
            return this;
        }
        /// <summary>
        /// 添加一条表达式
        /// </summary>
        protected ExprBuilder AddExpr(Expression expression)
        {
            EInfo.AddExpr(expression);
            return this;
        }
        /// <summary>
        /// 添加一条有返回值的表达式
        /// </summary>
        public ExprBuilder AddExprForLast(Expression expression)
        {
            EInfo.AddExprForLast(expression);
            return this;
        }
        public ExprBuilder AddParameter(Type type, string name = null)
        {
            PInfo.AddParameter(type, name);
            return this;
        }
        public ExprBuilder AddParameter(Type type, out ParameterExpression parameter, string name)
        {
            PInfo.AddParameter(type, out parameter, name);
            return this;
        }
        public ExprBuilder AddParameter(ParameterExpression parameter)
        {
            PInfo.AddParameter(parameter);
            return this;
        }
        /// <summary>
        /// 变量必须赋值后才能使用
        /// </summary>
        public ExprBuilder DeclareVariable(Type type, string name = null, Expression value = null)
        {
            PInfo.DeclareVariable(type,out var variable, name);
            if (value == null) return this;
            value = value.EnsureConvert(type);
            EInfo.Assign(variable, value);
            return this;
        }
        /// <summary>
        /// 变量必须赋值后才能使用
        /// </summary>
        public ExprBuilder DeclareVariable(Type type, out ParameterExpression variable, string name, Expression value = null)
        {
            PInfo.DeclareVariable(type, out variable, name);
            if (value == null) return this;
            value = value.EnsureConvert(type);
            EInfo.Assign(variable, value);
            return this;
        }
        public ExprBuilder DeclareVariable(ParameterExpression variable, Expression value = null)
        {
            PInfo.DeclareVariable(variable);
            if (value == null) return this;
            EInfo.Assign(variable, value);
            return this;
        }
        public ExprBuilder GetOrAddVariable(Type type, out ParameterExpression parameter, string name, Expression value=null)
        {
            PInfo.GetOrAddVariable(type, out parameter, name);
            if (value == null) return this;
            value = value.EnsureConvert(type);
            EInfo.Assign(parameter, value);
            return this;
        }
        /// <summary>
        /// 为对象赋值，如果value为null会传入上一个有效值
        /// <para>需要注意的是，不要在InvokeFunc后使用Assign，否则会调用两次Func。使用Assign(target,BuildInvokeFunc(...))替代</para>
        /// </summary>
        public ExprBuilder Assign(Expression target, Expression value = null)
        {
            value = value.EnsureConvert(target.Type);
            EInfo.Assign(target, value);
            return this;
        }
        public ExprBuilder AddAssign(Expression target, Expression value = null)
        {
            value = value.EnsureConvert(target.Type);
            EInfo.AddAssign(target, value);
            return this;
        }

        /// <summary>
        /// 将自身转为Block或返回值表达式。
        /// </summary>
        protected internal Expression ToSingleExpression(Type defaultReturnType, out Expression returnValue)
        {
            returnValue = EInfo.GetOrDefaultReturnValue(defaultReturnType);
            if (EInfo._expressions.Count == 0) return returnValue;
            //只要有语句就必须包裹，防止有未包含的变量
            return Expression.Block(PInfo._variables, EInfo._expressions);
        }
        protected internal Expression ToSingleExpression(Type defaultReturnType)
        {
            if (EInfo._expressions.Count == 0) return EInfo.GetOrDefaultReturnValue(defaultReturnType);
            return Expression.Block(PInfo._variables, EInfo._expressions);
        }

        public ExprBuilder If(Expression condition, Action<ExprBuilder> thenBranch, Action<ExprBuilder> elseBranch = null)
        {
            var thenBuilder = new ExprBuilder(this);
            thenBranch?.Invoke(thenBuilder);
            var thenExpr = thenBuilder.ToSingleExpression(typeof(void), out var thenReturn);
            bool thenHasReturn = !ExpressionUtility.IsNullOrEmpty(thenReturn);
            bool hasElseBranch = elseBranch != null;

            if (hasElseBranch)
            {
                var elseBuilder = new ExprBuilder(this);
                elseBranch.Invoke(elseBuilder);
                var elseExpr = elseBuilder.ToSingleExpression(typeof(void), out var elseReturn);
                var ifExpr = Expression.Condition(condition, thenExpr, elseExpr);
                bool elseHasReturn = !ExpressionUtility.IsNullOrEmpty(elseReturn);
                bool hasReturn = thenHasReturn && elseHasReturn;
                return hasReturn ? AddExprForLast(ifExpr) : AddExpr(ifExpr);
            }
            else
            {
                var ifExpr = Expression.IfThen(condition, thenExpr);
                return AddExpr(ifExpr);
            }
        }

        public ExprBuilder While(Expression condition, Action<ExprBuilder> body = null, LabelTarget breakLabel = null, LabelTarget continueLabel = null)
        {
            // 生成唯一标签名防止冲突
            var labelPrefix = $"While_{Guid.NewGuid():N}";
            breakLabel ??= Expression.Label($"{labelPrefix}_break");
            continueLabel ??= Expression.Label($"{labelPrefix}_continue");

            var bodyBuilder = new ExprBuilder(this);
            bodyBuilder.EInfo._breakLabel = breakLabel;
            bodyBuilder.EInfo._continueLabel = continueLabel;

            body?.Invoke(bodyBuilder);
            var bodyBlock = bodyBuilder.ToSingleExpression(typeof(void));
            //标准的while(condition)
            var loopBody = Expression.Block(
                Expression.IfThen(Expression.Not(condition), Expression.Break(breakLabel)),
                bodyBlock);
            var loop = Expression.Loop(loopBody, breakLabel, continueLabel);
            return AddExpr(loop);
        }

        public LambdaExpression Lambda()
        {
            var body = this.ToSingleExpression(typeof(void));
            return Expression.Lambda(body, PInfo._parameters);
        }

        public Expression<TDelegate> Lambda<TDelegate>() where TDelegate : Delegate
        {
            var delegateMethod = typeof(TDelegate).GetMethod("Invoke");

            var delegateParams = delegateMethod.GetParameters();

            if (PInfo._parameters.Count != delegateParams.Length)
                new ArgumentException($"{typeof(TDelegate).Name}参数数量不匹配: 预期{delegateParams.Length}，实际{PInfo._parameters.Count}").Throw();

            for (int i = 0; i < PInfo._parameters.Count; i++)
            {
                if (PInfo._parameters[i].Type != delegateParams[i].ParameterType)
                    new ArgumentException($"{typeof(TDelegate).Name}参数类型不匹配: 参数{i} 预期{delegateParams[i].ParameterType}，实际{PInfo._parameters[i].Type}").Throw();
            }
            var body = ToSingleExpression(delegateMethod.ReturnType);

            return Expression.Lambda<TDelegate>(body, PInfo._parameters);
        }

        public ExprBuilder StartDebug()
        {
            _debugMode = true;
            return this;
        }
        public ExprBuilder StopDebug()
        {
            _debugMode = true;
            return this;
        }

        /// <summary>
        /// 插入自定义重写逻辑
        /// </summary>
        public ExprBuilder Rewrite(Func<Expression, Expression> rewriteRule, out List<Expression> rewritten)
        {
            EInfo.Rewrite(rewriteRule, out rewritten);
            return this;
        }
        public ExprBuilder Break()
        {
            EInfo.Break();
            return this;
        }
        public ExprBuilder Continue()
        {
            EInfo.Continue();
            return this;
        }
        public ExprBuilder Clear()
        {
            PInfo.Clear();
            EInfo.Clear();
            return this;
        }
    }

    //以下功能依赖于其他ExprBuilder进行优化，因此这些功能绝对不能用于优化过程
    public partial class ExprBuilder
    {
        protected internal ExprBuilder CallMethod(Type type, string methodName, bool isStatic, Expression instance = null, Type[] genericArgs = null, params Expression[] args)
        {
            if (!isStatic && instance == null)
            {
                new ArgumentNullException("实例方法缺少实例").Throw();
                return this;
            }
            var validArgs = args.Where(e => e != null).ToArray();
            var method = TypeUtility.FindBestMatchMethod(type, methodName, isStatic, genericArgs, validArgs.Select(a => a.Type).ToArray());
            var call = Expression.Call(instance, method, validArgs);
            return method.ReturnType == typeof(void) ? AddExpr(call) : AddExprForLast(call);
        }
        public ExprBuilder CallStaticMethod(Type type, string methodName, Type[] genericArgs = null, params Expression[] args)
            => CallMethod(type, methodName, true, null, genericArgs, args);
        public ExprBuilder CallInstanceMethod(Expression instance, string methodName, Type[] genericArgs = null, params Expression[] args)
            => CallMethod(instance.Type, methodName, false, instance, genericArgs, args);

        public ExprBuilder EnsureConvert(Type targetType, MethodInfo method = null)
        {
            if (EInfo._lastValueExpression is null)
            {
                new InvalidOperationException("喵了个咪！想转换空气表达式？").Throw();
                return this;
            }
            //比如说上一条是Assign，那么_last是target表达式
            return AddExprForLast(EInfo._lastValueExpression.EnsureConvert(targetType, method));
        }
        /// <summary>
        /// 获取一个执行式
        /// </summary>
        public InvocationExpression BuildInvoke<D>(Expression<D> expression, params Expression[] args) where D : Delegate
        {
            var needParameters = expression.Parameters;
            if (args.Length != needParameters.Count)
            {
                new ArgumentException($"参数数量不匹配: 预期{expression.Parameters.Count()}，实际{args.Length}").Throw();
                return null;
            }
            for (int i = 0; i < needParameters.Count; i++)
            {
                args[i] = args[i].EnsureConvert(needParameters[i].Type);
            }

            var visitor = _debugMode ? new DebugBuilderVisitor(PInfo) : new BuilderVisitor(PInfo);
            var rewrittenlambda = visitor.Visit(expression);
            //这个方法默认需要执行一次lambda
            var invokeLambda = Expression.Invoke(rewrittenlambda, Array.AsReadOnly(args));
            return invokeLambda;
        }
        public ExprBuilder Invoke<D>(Expression<D> expression, params Expression[] args) where D : Delegate
        {
            var invokeLambda = BuildInvoke(expression, args);
            return expression.ReturnType == typeof(void) ? AddExpr(invokeLambda) : AddExprForLast(invokeLambda);
        }
        public ExprBuilder Using(ParameterExpression resource, Action<ExprBuilder> body)
        {
            var disposeMethod = BCache.BTypeCache.MethodInfo("IDisposable.Dispose", _ => typeof(IDisposable).GetMethod("Dispose"));
            var bodyBuilder = new ExprBuilder(this);
            body?.Invoke(bodyBuilder);
            var tryExpr = Expression.TryFinally(bodyBuilder.ToSingleExpression(typeof(void)), Expression.Call(resource, disposeMethod));
            return AddExpr(tryExpr);
        }
    }
}